import { defineStore } from "pinia";
import { getToken, removeToken, setToken } from "../utils/token-utils";
import type { UserInfoState } from "./interface";
import { staticRoutes, allAsyncRoutes, anyRoutes } from "@/router/routes";
import { reqLogin, reqLogout, reqUserInfo } from "@/api/user";
import type { RouteRecordRaw } from "vue-router";
import router from "@/router";
import { cloneDeep } from "lodash";

//1. 封装一个函数,用来根据用户的权限过滤异步路由表
//接受两个参数,一个是异步路由表 一个是用户权限的路由数组
function filterAsyncRoutes(
	allAsyncRoutes: Array<RouteRecordRaw>,
	routeNames: string[]
) {
	//逻辑就是根据用户的路由数组的值,过滤异步路由,得到过出来的符合条件的异步路由中的路由组成的数组
	return allAsyncRoutes.filter((item) => {
		//判断当前1级路由item的name有没有在用户权限路由数组中出现
		if (routeNames.includes(item.name as string)) {
			//递归一定要写条件,否则会出现栈溢出
			//没有子组件就停止
			if (item.children?.length) {
				//如果当前的1级路由确实出现在了当前用户的权限路由数组中,则还要看当前符合条件的1级路由中二级路由有哪些也是符合条件的
				//把二级路由过滤的结果得到,作为一级的路由要携带走的child子路由
				item.children = filterAsyncRoutes(
					item.children as RouteRecordRaw[],
					routeNames
				);
			}

			//filter只有在内部函数返回true的时候,才会进行过滤出来
			return true;
		}
	});
}

//2.因为添加新的路由映射关系只有addRoute方法,每次只能添加一个,所以我们可以封装一个addRoutes方法,每次能添加多个
//该方法接受一个参数,就是要添加到路由映射上的路由表数组
function addRoutes(routes: Array<RouteRecordRaw>) {
	routes.forEach((item) => {
		router.addRoute(item);
	});
}

//3.封装一个函数，当退出登录的时候，对动态路由清理。
function resetRoutes() {
	//排他法，先清理掉所有的映射关系，然后再添加常量路由的映射关系
	//路由对象有一个removeRoute方法,用来删除某个映射关系,参数是当前路由的name属性值
	//路由对象有一个getRoutes方法,用来得到所有的映射关系
	const routes = router.getRoutes(); //当前所有的映射关系
	//遍历删除
	routes.forEach((item) => {
		router.removeRoute(item.name as string);
	});

	//然后把静态路由和任意路由重新添加给路由对象
	addRoutes(staticRoutes);
	//不能在退出登录排他的时候 重新添加任意路由,因为任意路由只能最后添加,我们如果现在添加了,则未来再去添加异步路由的时候,异步路由就在后边了,匹配就会出现问题
	// addRoutes(anyRoutes);
}

/**
 * 用户信息
 * @methods setUserInfos 设置用户信息
 */
export const useUserInfoStore = defineStore("userInfo", {
	state: (): UserInfoState => ({
		token: getToken() as string,
		userInfo: {
			routes: [], //当前用户的路由权限(菜单权限)
			buttons: [], //当前用户的按钮权限
			roles: [], //当前用户的角色
			name: "",
			avatar: "",
		},
		menuRoutes: staticRoutes,
	}),

	actions: {
		//登录的方法
		/* 
      分析login登录需要做的事情
        - 发送请求,得到token
        - 把token保存在state中
        - token是要做数据持久化的,所以在localStorage中也保存一份token
    */
		async login(username: string, password: string) {
			const result = await reqLogin(username, password);
			this.token = result.token;
			setToken(result.token);
		},

		/*
      获取用户信息的逻辑
        - 发送请求
        - 拿到用户信息并交给state保管

    */
		async getInfo() {
			const userInfo = await reqUserInfo();
			// console.log(userInfo);
			this.userInfo = userInfo;

			//此时拿到用户的路由权限数组 routes
			//也可以拿到异步路由表
			//我们就进行对比,看一下异步路由表中哪些路由符合权限数组,如果符合,则把这个异步路由添加到静态路由中
			const filterRoutes = filterAsyncRoutes(
				cloneDeep(allAsyncRoutes),
				userInfo.routes
			);
			// console.log(re, "re");
			//拿到过滤出来的路由信息以后,把路由添加到静态路由中
			/* filterRoutes.forEach((item) => {
        staticRoutes.push(item);
      }); */

			//根据路由更新菜单(只是更新菜单,但是路由的映射关系是没有的)
			this.menuRoutes = staticRoutes.concat(filterRoutes, anyRoutes);

			//新增路由映射关系(把我们过滤出来的异步路由和任意路由进行合并,然后添加到路由的映射关系中)
			addRoutes(filterRoutes.concat(anyRoutes));
		},

		/* 
      退出登录逻辑
        - 发送退出登录请求
        - 清空本地数据
    */
		async logout() {
			await reqLogout();
			//数据和方法都在当前的store对象上,this指向store对象
			this.reset();
		},

		reset() {
			// 删除local中保存的token
			removeToken();
			// 提交重置用户信息的mutation
			this.token = "";
			this.userInfo = {
				routes: [],
				buttons: [],
				roles: [],
				name: "",
				avatar: "",
			};

			//退出登录的时候,我们也要重置一下路由映射关系
			resetRoutes();
		},
	},
});
